﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EzDesk.Apps.Model.Attendance;
using EzDesk.Utility.Extensions;
using EzDesk.Utility.Json;
using EzDesk.Utility.Log;
using EzDesk.Utility.ServiceStatusInfo;
using EzDesk.Apps.Data.Attendance;

namespace EzDesk.Apps.ServiceImpl.Attendance
{
    public partial class AttendanceService
    {
        #region [ private methods ]


        #region [ feed methods ]
        /// <summary>
        /// 申请新的假期之后，发送Feed
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <param name="applyText"></param>
        private void sendApplyLeaveFeed(LeaveInfo leaveInfo)
        {
            var senderUser = _ezApiClient.GetSimpleUserById(leaveInfo.UserId);
            var feedData = new Dictionary<string, string>
                               {
                                   {"senderName", senderUser.Name},
                                   {"leaveId", leaveInfo.Id.ToString()},
                                   {"leaveTypeName", leaveInfo.LeaveTypeName},
                                   {
                                       "applyText",
                                       string.IsNullOrEmpty(leaveInfo.Description)
                                           ? ""
                                           : string.Format("\"{0}\"", leaveInfo.Description)
                                       }
                               };

            // 发送目标为所有所有公司boss
            var memberString = _ezApiClient.GetGroupByName("core_boss").Memberstring;

            var feedId = _ezApiClient.SendFeed(
                FeedTemplateStr_attendance_applyLeave,
                JsonHelper.ConvertToJson(feedData),
                memberString
                );

            _ezApiClient.SetFeedRecord(leaveInfo.Id, FeedTemplateStr_attendance_applyLeave, memberString, feedId, null);
        }

        /// <summary>
        /// 审批假期成功之后，发送Feed
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <param name="applyText"></param>
        private void sendAuditLeaveSuccFeed(Guid auditUserId, LeaveInfo leaveInfo)
        {
            var auditUser = _ezApiClient.GetSimpleUserById(auditUserId);

            var feedData = new Dictionary<string, string>
                               {
                                   {"auditUserName", auditUser.Name},
                                   {"currentTime", DateTime.Now.ToEpoch().ToString()},
                                   {"leaveTypeName", leaveInfo.LeaveTypeName},
                                   {"startOn", leaveInfo.StartTime.ToFullDateTime()},
                                   {"endOn", leaveInfo.EndTime.ToFullDateTime()},
                                   {
                                       "auditText",
                                       string.IsNullOrEmpty(leaveInfo.AuditNote)
                                           ? ""
                                           : string.Format("附言: \"{0}\"<ez:br />", leaveInfo.AuditNote)
                                       },
                               };

            // 发送目标 请假申请发出者
            var memberString = leaveInfo.UserId.ToString();

            var feedId = _ezApiClient.SendFeed(
                FeedTemplateStr_attendance_auditLeaveSucc,
                JsonHelper.ConvertToJson(feedData),
                memberString
                );
        }

        /// <summary>
        /// 审批假期拒绝之后，发送Feed
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <param name="applyText"></param>
        private void sendAuditLeaveRejectedFeed(Guid auditUserId, LeaveInfo leaveInfo)
        {
            var auditUser = _ezApiClient.GetSimpleUserById(auditUserId);

            var feedData = new Dictionary<string, string>
                               {
                                   {"auditUserName", auditUser.Name},
                                   {"currentTime", DateTime.Now.ToEpoch().ToString()},
                                   {"leaveTypeName", leaveInfo.LeaveTypeName},
                                   {
                                       "auditText",
                                       string.IsNullOrEmpty(leaveInfo.AuditNote)
                                           ? ""
                                           : string.Format("\"{0}\"<ez:br />", leaveInfo.AuditNote)
                                       },
                               };

            // 发送目标 请假申请发出者
            var memberString = leaveInfo.UserId.ToString();

            var feedId = _ezApiClient.SendFeed(
                FeedTemplateStr_attendance_auditLeaveRejected,
                JsonHelper.ConvertToJson(feedData),
                memberString
                );
        }

        #endregion

        #region [ converTo Methods ]
        private LeaveInfo convertToLeaveInfoViewModel(LeaveInfo leaveInfo)
        {
            if (leaveInfo != null)
            {
                EzDeskAttendanceContainer.Go(
                    container =>
                        {
                            if (leaveInfo.UserId != Guid.Empty)
                            {
                                leaveInfo.User = _ezApiClient.GetSimpleUserById(leaveInfo.UserId);
                            }
                            if (leaveInfo.AuditUserId.HasValue)
                            {
                                leaveInfo.AuditUser = _ezApiClient.GetSimpleUserById(leaveInfo.AuditUserId.Value);
                            }
                            var leaveType = container.LeaveTypeSet.FirstOrDefault(p => p.Id == leaveInfo.LeaveTypeId);
                            leaveInfo.LeaveTypeName = leaveType.LeaveTypeName;
                            leaveInfo.IsForWork = leaveType.IsForWork;
                        });

            }
            return leaveInfo;
        }
        #endregion

        /// <summary>
        /// 获得一天的这一天信息
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private static DayInfo getDayInfo(DateTime date, Guid userId, EzDeskAttendanceContainer container)
        {
            return getDayInfos(date, date, userId, container).FirstOrDefault();
        }

        private static List<DayInfo> getDayInfos(DateTime startDate, DateTime endDate, Guid userId, EzDeskAttendanceContainer container)
        {
            var startday = startDate.Date;
            var endDay = endDate.Date;

            var dayInfos = new List<DayInfo>();
            var leagalHolidays = container.LegalHolidaySet.Where(p => p.StartDate <= endDay && startday <= p.EndDate).ToList();
            var extraShifts = container.ExtraShiftSet.Where(p => p.StartDate <= endDay && startday <= p.EndDate).ToList();

            for (var theDay = startday; theDay <= endDay; theDay = theDay.AddDays(1))
            {
                var date = theDay;
                dayInfos.Add(
                    new DayInfo(date)
                        {
                            HolidayInfo =
                                leagalHolidays.FirstOrDefault(p => p.StartDate <= date && date <= p.EndDate),
                            ExtraShiftInfo =
                                extraShifts.FirstOrDefault(p => p.StartDate <= date && date <= p.EndDate)
                        });
            }
            return dayInfos;
        }

        /// <summary>
        /// 获得某人某天的假期状况
        /// </summary>
        /// <param name="date"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        private DayLeaveInfo getDayLeaveInfo(DateTime date, Guid userId, EzDeskAttendanceContainer container)
        {
            return getDayLeaveInfos(date, date, userId, container).FirstOrDefault();
        }

        private List<DayLeaveInfo> getDayLeaveInfos(DateTime startDate, DateTime endDate, Guid userId, EzDeskAttendanceContainer container)
        {
            var startDay = startDate.Date;
            var endDay = endDate.Date;

            var dayLeaveInfos = new List<DayLeaveInfo>();
            // 初始化DayLeaveInfo
            for (var theDay = startDay; theDay <= endDay; theDay = theDay.AddDays(1))
            {
                dayLeaveInfos.Add(
                    new DayLeaveInfo(theDay)
                    );
            }

            // 获得这段时间内涉及的所有假期
            var leaves =
                container.LeaveInfoSet
                  .Where(p => p.UserId == userId && (p.StartTime <= endDate && p.EndTime >= startDate))
                  .ToList();
            foreach (var leaveInfo in leaves)
            {
                startDay = leaveInfo.StartTime.Date;
                endDay = leaveInfo.EndTime.Date;
                for (var theDay = startDay; theDay <= endDay; theDay = theDay.AddDays(1))
                {
                    var day = theDay;
                    var theDayOnDutyTime = attendanceConfig.StdOnDutyTime.ToDateTime(day);
                    var theDayOffDutyTime = attendanceConfig.StdOffDutyTime.ToDateTime(day);

                    var dayLeaveInfo = dayLeaveInfos.First(p => p.Date == day);
                    // todo: 将LeaveType做成navigation property
                    var leaveType = container.LeaveTypeSet.FirstOrDefault(p => p.Id == leaveInfo.LeaveTypeId);

                    var leaveInfoItem = new DayLeaveInfoItem()
                                            {
                                                Day = day,
                                                StartTime =
                                                    theDayOnDutyTime > leaveInfo.StartTime
                                                        ? theDayOnDutyTime
                                                        : leaveInfo.StartTime,
                                                EndTime =
                                                    theDayOffDutyTime < leaveInfo.EndTime
                                                        ? theDayOffDutyTime
                                                        : leaveInfo.EndTime,
                                                LeaveId = leaveInfo.Id,

                                                IsForWork = leaveType.IsForWork,
                                                LeaveTypeName = leaveType.LeaveTypeName,
                                            };
                    dayLeaveInfo.Leaves.Add(leaveInfoItem);

                    dayLeaveInfo.IsWholeDay = (leaveInfo.StartTime <= theDayOnDutyTime &&
                                               leaveInfo.EndTime >= theDayOffDutyTime);
                }
            }

            return dayLeaveInfos;
        }

        /// <summary>
        /// 判断假期是否重复
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        private bool isDuplicateLeave(DateTime startTime, DateTime endTime, Guid userId)
        {
            // 找出没审核或者是审核没通过的
            return EzDeskAttendanceContainer.Go(
                container => container.LeaveInfoSet
                                 .Where(p => p.UserId == userId)
                                 .Where(p => p.StartTime <= endTime && p.EndTime >= startTime)
                                 .Where(p => !p.IsAudited || p.IsAgreed)
                                 .FirstOrDefault() != null);
        }

        #endregion

        #region [ about Leaves ]

        /// <summary>
        /// 获得LeaveInfo列表
        /// </summary>
        private List<LeaveInfo> getLeaveInfoList(
             DateTime? startTime, DateTime? endTime,
            DateTime? createTimeStart,
            DateTime? createTimeEnd,
            Guid? userId,
            EzDeskAttendanceContainer container,
            bool? isAudited = null,
            bool? isAgreed = null)
        {
            IQueryable<LeaveInfo> query = container.LeaveInfoSet;

            if (startTime.HasValue)
            {
                query = query.Where(p => p.StartTime >= startTime);
            }

            if (endTime.HasValue)
            {
                query = query.Where(p => p.EndTime <= endTime);
            }

            if (createTimeStart.HasValue)
            {
                query = query.Where(p => p.CreateOn >= createTimeStart);
            }

            if (createTimeEnd.HasValue)
            {
                query = query.Where(p => p.CreateOn <= createTimeEnd);
            }

            if (userId.HasValue)
            {
                query = query.Where(p => p.UserId == userId.Value);
            }

            if (isAudited.HasValue)
            {
                query = query.Where(p => p.IsAudited == isAudited.Value);

                if (isAgreed.HasValue)
                {
                    query = query.Where(p => p.IsAgreed == isAgreed.Value);
                }
            }
            var leaveInfos = query.ToList();

            return leaveInfos.Select(convertToLeaveInfoViewModel).ToList();
        }

        /// <summary>
        /// 获得当前所有的请假类型
        /// </summary>
        /// <returns></returns>
        public StatusInfo<List<LeaveType>> GetAllLeaveTypes()
        {
            return StatusInfo.Wrap(
                () =>
                EzDeskAttendanceContainer.Go(
                    container => container.LeaveTypeSet
                                     .OrderBy(p => p.OrderValue)
                                     .ToList()
                    ));
        }

        /// <summary>
        /// 通过ID获得一种请假类型
        /// </summary>
        /// <param name="leaveTypeId"></param>
        /// <returns></returns>
        public StatusInfo<LeaveType> GetLeaveTypeyById(Guid leaveTypeId)
        {
            return StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.Go(
                    container => container.LeaveTypeSet.FirstOrDefault(p => p.Id == leaveTypeId))
                );
        }

        /// <summary>
        /// 获得某人一天的假期等情况
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        public StatusInfo<DayInfo> GetDayInfo(Guid userId, DateTime date)
        {
            return StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.Go(
                    container => getDayInfo(date, userId, container)
                          ));
        }

        public StatusInfo<List<DayInfo>> GetDayInfos(Guid userId, DateTime startDate, DateTime endDate)
        {
            LogEx.TRACE("AttendanceService - GetDayInfos", "请求拿一个用户一个月的Dayinfos");

            var result = StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.Go(
                    contaienr => getDayInfos(startDate, endDate, userId, contaienr)
                          ));

            LogEx.TRACE("AttendanceService - GetDayInfos", "拿一个用户一个月的Dayinfos完成");
            return result;
        }



        /// <summary>
        /// 获得某人一天中的请假情况
        /// </summary>
        /// <param name="CurrentUserId"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        public StatusInfo<DayLeaveInfo> GetDayLeaveInfo(Guid userId, DateTime date)
        {
            return StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.Go(
                    container => getDayLeaveInfo(date, userId, container)
                          )
                );
        }
        /// <summary>
        /// 获得好几天的一天中假期状况
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="startDay"></param>
        /// <param name="endDay"></param>
        /// <returns></returns>
        public StatusInfo<List<DayLeaveInfo>> GetDayLeaveInfos(Guid userId, DateTime startDate, DateTime endDate)
        {
            return StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.Go(
                    container =>
                        {
                            LogEx.TRACE("AttendanceService - GetDayLeaveInfos", "请求拿一个用户一个月的LeaveInfos");
                            var result = getDayLeaveInfos(startDate, endDate, userId, container);
                            LogEx.TRACE("AttendanceService - GetDayLeaveInfos", "拿一个用户一个月的LeaveInfos结束");
                            return result;
                        })
                );
        }



        /// <summary>
        /// 通过ID获得一个请假
        /// </summary>
        /// <param name="leaveId"></param>
        /// <returns></returns>
        public StatusInfo<LeaveInfo> GetLeaveInfoById(Guid leaveId)
        {
            return StatusInfo.Wrap(
                () => convertToLeaveInfoViewModel(
                    EzDeskAttendanceContainer.Go(
                        container =>
                        container.LeaveInfoSet.FirstOrDefault(p => p.Id == leaveId))));
        }

        /// <summary>
        /// 获得一个请假记录
        /// </summary>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="userId">请假人ID</param>
        /// <param name="isAudited">是否已经审核</param>
        public StatusInfo<List<LeaveInfo>> GetLeaveInfoList(
            DateTime? startTime,
            DateTime? endTime,
            DateTime? createTimeStart,
            DateTime? createTimeEnd,
            Guid? userId,
            bool? isAudited,
            bool? isAgreed)
        {
            return StatusInfo.Wrap(
                () =>
                 EzDeskAttendanceContainer.Go(
                    container => getLeaveInfoList(startTime, endTime, createTimeStart, createTimeEnd, userId, container,
                                                  isAudited, isAgreed)
                                     .Select(convertToLeaveInfoViewModel)
                                     .ToList())
                );
        }

        /// <summary>
        /// 申请一个新的假期
        /// </summary>
        /// <param name="info"></param>
        public StatusInfo ApplyLeave(LeaveInfo info)
        {
            return StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.Go(
                    container =>
                        {
                            if (info.StartTime >= info.EndTime)
                            {
                                throw new Exception("假期开始时间必须早于假期结束时间。");
                            }
                            if (isDuplicateLeave(info.StartTime, info.EndTime, info.UserId))
                            {
                                throw new Exception("假期有重复");
                            }
                            //todo: 验证其他字段
                            info.CreateOn = DateTime.Now;
                            var newInfoId = info.Id = Guid.NewGuid();

                            container.LeaveInfoSet.AddObject(info);
                            container.SaveChanges();

                            info = convertToLeaveInfoViewModel(info);
                            sendApplyLeaveFeed(info);

                            // 向负责审批假期的员工发出Notification
                        })
                );
        }

        public StatusInfo<bool> IsDuplicateLeave(DateTime startTime, DateTime endTime, Guid userId)
        {
            return StatusInfo.Wrap(
                () => isDuplicateLeave(startTime, endTime, userId)
                );
        }

        /// <summary>
        /// 对一个请假进行审批
        /// </summary>
        /// <param name="leaveId">请假ID</param>
        /// <param name="isAgreed">是否同意请假</param>
        /// <param name="auditNote">审批备注</param>
        /// <param name="auditId">审批人Id</param>
        public StatusInfo AuditLeave(Guid leaveId, bool isAgreed, string auditNote, Guid auditId)
        {
            return StatusInfo.Wrap(
                () => EzDeskAttendanceContainer.Go(
                    container =>
                        {
                            var leaveInfo = container.LeaveInfoSet.FirstOrDefault(p => p.Id == leaveId);

                            if (leaveInfo == null)
                            {
                                throw new Exception("未找到对应的信息");
                            }

                            if (leaveInfo.IsAudited)
                            {
                                throw new Exception("假期已经被审批过");
                            }

                            leaveInfo.IsAudited = true;
                            leaveInfo.AuditUserId = auditId;
                            leaveInfo.AuditNote = auditNote;
                            leaveInfo.AuditTime = DateTime.Now;
                            leaveInfo.IsAgreed = isAgreed;

                            container.SaveChanges();
                           

                            if (isAgreed)
                            {
                                // 审批成功
                                var startDay = leaveInfo.StartTime.Date;
                                var endDay = leaveInfo.EndTime.Date;
                                for (var theDay = startDay; theDay <= endDay; theDay = theDay.AddDays(1) )
                                {
                                    var day = theDay;
                                    refreshDayAttendanceEvaluation(leaveInfo.UserId, day, container);
                                }

                                // 向被审批的对象发出通知
                                sendAuditLeaveSuccFeed(auditId, convertToLeaveInfoViewModel(leaveInfo));
                            }
                            else
                            {
                                // 发出被拒绝通知
                                sendAuditLeaveRejectedFeed(auditId, convertToLeaveInfoViewModel(leaveInfo));
                            }

                            // 取消所有相关的要求审批的Notificaiton
                            var auditLeaveFeedId = Guid.Parse(
                                _ezApiClient.GetFeedRecord(leaveId, FeedTemplateStr_attendance_applyLeave,
                                                           null)
                                );
                            var memberstring = _ezApiClient.GetGroupByName("core_boss").Memberstring;
                            _ezApiClient.SetFeedIsAvailable(auditLeaveFeedId, memberstring, false);

                            // todo: 通知所有人假期生效
                        }));
        }

        #endregion
    }
}
